﻿#region Using Statements
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Design;
using SqEngine.Components;
using sqengine.Components;
using sqengine.Engine.Common;
using System.IO;
using System.IO.Compression;
using System.Text;
using System.Net;
using System.Reflection;
using System.Text.RegularExpressions;
using System.Security.Cryptography;


#endregion


namespace sqengine.Engine.Common
{
    class Functions
    {
        public static List<string> GetFilesRecursive(string directory, string ext)
        {
            List<string> result = new List<string>();
            Stack<string> stack = new Stack<string>();

            stack.Push(directory);


            while (stack.Count > 0)
            {
                string dir = stack.Pop();

                try
                {
                    result.AddRange(Directory.GetFiles(dir, ext));
                    foreach (string dn in Directory.GetDirectories(dir))
                    {
                        stack.Push(dn);
                    }
                }
                catch
                {
                }
            }
            return result;

        }

        public static List<string> GetDirectoryList(string directory)
        {
            List<string> _list = new List<string>();
            DirectoryInfo MyRoot = new DirectoryInfo(directory);

            DirectoryInfo[] MySub = MyRoot.GetDirectories();

            foreach (DirectoryInfo D in MySub)
            {
                _list.Add(D.Name);

            }

            return _list;

        }

        public static List<string> GetFilesList(string directory)
        {
            return GetFilesList(directory, "*.*");
        }

        public static List<string> GetFilesList(string directory, string ext)
        {
            DirectoryInfo MyRoot = new DirectoryInfo(directory);
            List<string> _list = new List<string>();
            FileInfo[] MyFiles = MyRoot.GetFiles(ext);

            foreach (FileInfo F in MyFiles)
            {
                _list.Add(F.Name);
            }
            return _list;
        }


        //Utility Random

        /// <summary>
        /// Returns a number between two values.
        /// </summary>
        /// <param name="min">Lower bound value</param>
        /// <param name="max">Upper bound value</param>
        /// <returns>Random number between bounds.</returns>
        public static float RandomBetween(double min, double max)
        {
            Random random = new Random();
            return (float)(min + (float)random.NextDouble() * (max - min));
        }

        public static Vector3 RandomPosition(Vector3 minBoxPos, Vector3 maxBoxPos)
        {
            Random random = new Random();

            return new Vector3(
                     RandomBetween(minBoxPos.X, maxBoxPos.X),
                     RandomBetween(minBoxPos.Y, maxBoxPos.Y),
                     RandomBetween(minBoxPos.Z, maxBoxPos.Z));
        }

        public static Vector3 RandomDirection()
        {
            Random random = new Random();

            Vector3 direction = new Vector3(
                    RandomBetween(-1.0f, 1.0f),
                    RandomBetween(-1.0f, 1.0f),
                    RandomBetween(-1.0f, 1.0f));
            direction.Normalize();

            return direction;
        }

        /// <summary>
        /// Helper function chooses a random location on a triangle.
        /// </summary>
        public static Vector3 PickRandomPoint(
                  Vector3 position1,
                  Vector3 position2,
                  Vector3 position3)
        {
            Random random = new Random();

            float a = (float)random.NextDouble();
            float b = (float)random.NextDouble();

            if (a + b > 1)
            {
                a = 1 - a;
                b = 1 - b;
            }

            return Vector3.Barycentric(position1, position2, position3, a, b);
        }

        // Code
        public static Color[] colors = new Color[] { 
            Color.White, 
            Color.Black,
            Color.Navy,
            Color.Green,
            Color.Red,
            Color.Brown,
            Color.Purple,
            Color.Orange,
            Color.Yellow,
            Color.Lime,
            Color.Teal,
            Color.Aqua,
            Color.Blue,
            Color.Fuchsia,
            Color.Gray,
            Color.Silver
        };

        public static void DrawColorText(SpriteFont font, string str, Vector2 xy, SpriteBatch spriteBatch)
        {
            char chr_color = '$';
            string[] splits = str.Split(new char[] { chr_color }, StringSplitOptions.RemoveEmptyEntries);
            Color color = Color.White;
            int currentOffset = 0;

            if (splits.Length > 1 || str.Contains(chr_color.ToString()))
            {
                if (Char.IsNumber(splits[0][0]))
                {
                    int idx = 0;
                    int.TryParse(splits[0][0].ToString(), out idx);
                    color = colors[idx];

                    spriteBatch.DrawString(font, splits[0].Replace(char.Parse(idx.ToString()), ' ').Trim(), xy + new Vector2(currentOffset, 0) + new Vector2(1), Color.Black);
                    spriteBatch.DrawString(font, splits[0].Replace(char.Parse(idx.ToString()), ' ').Trim(), xy + new Vector2(currentOffset, 0), color);
                    currentOffset += (int)font.MeasureString(splits[0]).X;

                }

            }
            else
            {
                spriteBatch.DrawString(font, str, xy, color);
            }


        }
        public static void DrawChat(SpriteBatch spriteBatch, SpriteFont font, Vector2 xy, string str)
        {

            char chr_color = '$';
            string[] splits = str.Split(new char[] { chr_color }, StringSplitOptions.RemoveEmptyEntries);

            int currentOffset = 0;

            //No Memory load
            if (splits.Length > 1 || str.Contains(chr_color.ToString()))
            {
                for (int j = 0; j < splits.Length; j++)
                {
                    Color color = Color.White;

                    if (splits[j].Length > 2)
                    {
                        int number = 0;

                        // for double digits like 00 or 99
                        if (Char.IsNumber(splits[j][0]) && Char.IsNumber(splits[j][1]))
                        {
                            number = int.Parse(splits[j][0] + "") * 10 + int.Parse(splits[j][1] + "");
                            splits[j] = splits[j].Substring(2);
                        }

                        // for single digits 0-9
                        else if (Char.IsNumber(splits[j][0]) && !Char.IsNumber(splits[j][1]))
                        {
                            number = int.Parse(splits[j][0] + "");
                            splits[j] = splits[j].Substring(1);
                        }

                        if (number < colors.Length && number >= 0)
                        {
                            color = colors[number];
                        }

                    }
                    spriteBatch.DrawString(font, splits[j], xy + new Vector2(currentOffset, 0) + new Vector2(1), Color.Black);
                    spriteBatch.DrawString(font, splits[j], xy + new Vector2(currentOffset, 0), color);
                    currentOffset += (int)font.MeasureString(splits[j]).X;

                }
            }
            else
            {
                spriteBatch.DrawString(font, str, xy, colors[0]);

            }

             
        }

        public static Vector2 GetCenterOfScreen(Game game)
        {
            return new Vector2(game.GraphicsDevice.Viewport.Width / 2, game.GraphicsDevice.Viewport.Height / 2);
        }

        public static double ConvertBytesToMegabytes(long bytes)
        {
            return (bytes / 1024f) / 1024f;
        }

        public static double ConvertKilobytesToMegabytes(long kilobytes)
        {
            return kilobytes / 1024f;
        }


        public static string Compress(string text)
        {
            byte[] buffer = Encoding.UTF8.GetBytes(text);
            MemoryStream ms = new MemoryStream();
            using (GZipStream zip = new GZipStream(ms, CompressionMode.Compress, true))
            {
                zip.Write(buffer, 0, buffer.Length);
            }

            ms.Position = 0;
            MemoryStream outStream = new MemoryStream();

            byte[] compressed = new byte[ms.Length];
            ms.Read(compressed, 0, compressed.Length);

            byte[] gzBuffer = new byte[compressed.Length + 4];
            System.Buffer.BlockCopy(compressed, 0, gzBuffer, 4, compressed.Length);
            System.Buffer.BlockCopy(BitConverter.GetBytes(buffer.Length), 0, gzBuffer, 0, 4);
            return Convert.ToBase64String(gzBuffer);
        }

        public static MemoryStream CompressStream(Stream st)
        {
            st.Position = 0;
            StreamReader reader = new StreamReader(st);
            string xml_file = reader.ReadToEnd();
            st.Position = st.Length;
            xml_file = Compress(xml_file);
            byte[] byteArray = Encoding.ASCII.GetBytes(xml_file);
            MemoryStream comp_stream = new MemoryStream(byteArray);

            return comp_stream;
        }

        public static Stream DecompressStream(Stream st)
        {
            StreamReader reader = new StreamReader(st);
            string xml_file = reader.ReadToEnd();
            reader.Dispose();
            xml_file = Decompress(xml_file);
            byte[] byteArray = Encoding.ASCII.GetBytes(xml_file);
            MemoryStream comp_stream = new MemoryStream(byteArray);

            return comp_stream;
        }

        public static string Decompress(string compressedText)
        {
            try
            {
                byte[] gzBuffer = Convert.FromBase64String(compressedText);
                using (MemoryStream ms = new MemoryStream())
                {
                    int msgLength = BitConverter.ToInt32(gzBuffer, 0);
                    ms.Write(gzBuffer, 4, gzBuffer.Length - 4);

                    byte[] buffer = new byte[msgLength];

                    ms.Position = 0;
                    using (GZipStream zip = new GZipStream(ms, CompressionMode.Decompress))
                    {
                        zip.Read(buffer, 0, buffer.Length);
                    }

                    return Encoding.UTF8.GetString(buffer);
                }
            }
            catch
            {

                return compressedText;
            }
        }

        public static void SaveMemoryStream(MemoryStream ms, string FileName)
        {
            FileStream outStream;

            if (File.Exists(FileName))
            {
                outStream = new FileStream(FileName, FileMode.Truncate);
            }
            else
            {
                outStream = new FileStream(FileName, FileMode.OpenOrCreate);
            }

            ms.WriteTo(outStream);
            outStream.Flush();
            outStream.Close();
        }

        public static void SaveExceptionToFile(string filename, string header, object sourceclass, Exception ex)
        {
            StreamWriter st = new StreamWriter(filename, true);
            st.WriteLine("{0}: {1}", DateTime.Now.ToString(), header);
            st.WriteLine("{0}: Source class: {1}", DateTime.Now.ToString(), sourceclass);
            st.WriteLine("{0}: Exception logger", DateTime.Now.ToString());
            st.WriteLine("{0}: Message: {1}", DateTime.Now.ToString(), ex.Message);
            st.WriteLine("{0}: HelpLink: {1}", DateTime.Now.ToString(), ex.HelpLink);
            st.WriteLine("{0}: InnerException: {1}", DateTime.Now.ToString(), ex.InnerException);
            st.WriteLine("{0}: StackTrace: {1}", DateTime.Now.ToString(), ex.StackTrace);
            st.WriteLine("{0}: TargetSite: {1}", DateTime.Now.ToString(), ex.TargetSite);
            st.Close();

        }

        public static void TraceLogWrite(StreamWriter stream, bool write_header, string header, string message, bool close)
        {
            if (stream.BaseStream != null)
            {
                if (write_header)
                    stream.WriteLine("{0}.{1}: {2}", DateTime.Now.ToString(), DateTime.Now.Millisecond.ToString(), header);

                stream.WriteLine("{0}.{1}: {2}", DateTime.Now.ToString(), DateTime.Now.Millisecond.ToString(), message);

                if (close)
                    stream.Close();
            }


        }

        public static string EncryptData(string data, out byte[] desKey, out byte[] desIV)
        {
            MemoryStream output = new MemoryStream();
            byte[] byteData = new UnicodeEncoding().GetBytes(data);

            //Use the TripleDES symmetric encryption algorithm to encrypt our data. Without an IV, the 			
            //same input block of plaintext will encrypt to same output block of ciphertext. IV guarantees 
            //output of two identical plaintext blocks are different. 
            TripleDESCryptoServiceProvider des = new TripleDESCryptoServiceProvider();
            CryptoStream crypt = new CryptoStream(output, des.CreateEncryptor(), CryptoStreamMode.Write);

            //Assign our crypto-generated key and iv values to our output arguments
            desKey = des.Key; desIV = des.IV;
            crypt.Write(byteData, 0, byteData.Length);

            crypt.Close(); output.Close();
            return new UnicodeEncoding().GetString(output.ToArray());
        }

        public static string EncryptData(byte[] desKey, byte[] desIV, string data)
        {
            MemoryStream output = new MemoryStream();
            byte[] byteData = new UnicodeEncoding().GetBytes(data);

            //Use the TripleDES symmetric encryption algorithm to encrypt our data. Without an IV, the 			
            //same input block of plaintext will encrypt to same output block of ciphertext. IV guarantees 
            //output of two identical plaintext blocks are different. 
            TripleDESCryptoServiceProvider des = new TripleDESCryptoServiceProvider();
            CryptoStream crypt = new CryptoStream(output, des.CreateEncryptor(desKey, desIV), CryptoStreamMode.Write);
            crypt.Write(byteData, 0, byteData.Length);

            crypt.Close(); output.Close();
            return new UnicodeEncoding().GetString(output.ToArray());
        }

        public static string DecryptData(string data, byte[] desKey, byte[] desIV)
        {
            MemoryStream output = new MemoryStream();
            byte[] byteData = new UnicodeEncoding().GetBytes(data);

            //Use the TripleDES symmetric encryption algorithm to decrypt our data. In order for the ciphertext to be
            //successfully decrypted, the exact same key and iv must be used when initially encryted.
            TripleDESCryptoServiceProvider des = new TripleDESCryptoServiceProvider();
            CryptoStream crypt = new CryptoStream(output, des.CreateDecryptor(desKey, desIV), CryptoStreamMode.Write);
            crypt.Write(byteData, 0, byteData.Length);

            crypt.Close(); output.Close();
            return new UnicodeEncoding().GetString(output.ToArray());
        }

        public static string base64Encode(string data)
        {
            try
            {
                byte[] encData_byte = new byte[data.Length];
                encData_byte = System.Text.Encoding.UTF8.GetBytes(data);
                string encodedData = Convert.ToBase64String(encData_byte);
                return encodedData;
            }
            catch (Exception e)
            {
                throw new Exception("Error in base64Encode" + e.Message);
            }
        }

        public static string base64Decode(string data)
        {
            try
            {
                System.Text.UTF8Encoding encoder = new System.Text.UTF8Encoding();
                System.Text.Decoder utf8Decode = encoder.GetDecoder();

                byte[] todecode_byte = Convert.FromBase64String(data);
                int charCount = utf8Decode.GetCharCount(todecode_byte, 0, todecode_byte.Length);
                char[] decoded_char = new char[charCount];
                utf8Decode.GetChars(todecode_byte, 0, todecode_byte.Length, decoded_char, 0);
                string result = new String(decoded_char);
                return result;
            }
            catch (Exception e)
            {
                throw new Exception("Error in base64Decode" + e.Message);
            }
        }

        public static string GetCurrentPath()
        {

            return Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);

        }


        public static string CheckPasswordString(string passwd)
        {
            if (passwd.StartsWith("password:"))
                passwd = passwd.Replace("password:", string.Empty);

            return passwd;

        }


    }







    /// <summary>
    /// Provides a set of methods for rendering Rays.
    /// </summary>
    public static class RayRenderer
    {
        static VertexPositionColor[] verts = new VertexPositionColor[2];

        static VertexPositionColor[] arrowVerts = new VertexPositionColor[]
        {
            new VertexPositionColor(Vector3.Zero, Color.White),
            new VertexPositionColor(new Vector3(.5f, 0f, -.5f), Color.White),
            new VertexPositionColor(new Vector3(-.5f, 0f, -.5f), Color.White),
            new VertexPositionColor(new Vector3(0f, .5f, -.5f), Color.White),
            new VertexPositionColor(new Vector3(0f, -.5f, -.5f), Color.White),
        };
        static int[] arrowIndexs = new int[]
        {
            0, 1,
            0, 2,
            0, 3,
            0, 4,
        };

        static VertexDeclaration vertDecl;
        static BasicEffect effect;

        /// <summary>
        /// Renders a Ray for debugging purposes.
        /// </summary>
        /// <param name="ray">The ray to render.</param>
        /// <param name="length">The distance along the ray to render.</param>
        /// <param name="graphicsDevice">The graphics device to use when rendering.</param>
        /// <param name="view">The current view matrix.</param>
        /// <param name="projection">The current projection matrix.</param>
        /// <param name="color">The color to use drawing the ray.</param>
        public static void Render(
            Ray ray,
            float length,
            GraphicsDevice graphicsDevice,
            Matrix view,
            Matrix projection,
            Color color)
        {
            if (effect == null)
            {
                effect = new BasicEffect(graphicsDevice, null);
                effect.VertexColorEnabled = false;
                effect.LightingEnabled = false;
                vertDecl = new VertexDeclaration(graphicsDevice, VertexPositionColor.VertexElements);
            }

            verts[0] = new VertexPositionColor(ray.Position, Color.White);
            verts[1] = new VertexPositionColor(ray.Position + (ray.Direction * length), Color.White);

            effect.DiffuseColor = color.ToVector3();
            effect.Alpha = (float)color.A / 255f;

            effect.World = Matrix.Identity;
            effect.View = view;
            effect.Projection = projection;

            graphicsDevice.VertexDeclaration = vertDecl;

            effect.Begin();
            foreach (EffectPass pass in effect.CurrentTechnique.Passes)
            {
                pass.Begin();

                graphicsDevice.DrawUserPrimitives(PrimitiveType.LineList, verts, 0, 1);

                effect.World = Matrix.Invert(Matrix.CreateLookAt(
                    verts[1].Position,
                    verts[0].Position,
                    (ray.Direction != Vector3.Up) ? Vector3.Up : Vector3.Left));
                effect.CommitChanges();

                graphicsDevice.DrawUserIndexedPrimitives(PrimitiveType.LineList, arrowVerts, 0, 5, arrowIndexs, 0, 4);

                pass.End();
            }
            effect.End();
        }
    }

   
    class Motion
    {
        /// <summary>
        /// This returns a vector which lets the object fly straight to the target position.
        /// </summary>
        /// <param name="ActualPosition">Actual position of the moving object</param>
        /// <param name="TargetPosition">Position of the traget</param>
        /// <param name="Speed">Distance per frame</param>
        public static Vector3 Straight(Vector3 ActualPosition, Vector3 TargetPosition, float Speed)
        {
            // A Vector3 to hold the result velocity:
            Vector3 Velocity = Vector3.Zero;

            //The Direction to the TargetPosition:
            Vector3 Direction = ActualPosition - TargetPosition;

            //The distance between the two vectors:
            float Distance = Direction.Length();

            //This changes Direction into a Vector which points into the same Direction, but with the value '1'.
            Direction.Normalize();

            if (ActualPosition != TargetPosition)
            {
                //Velocity Calculation:
                if (Distance >= Speed)
                    Velocity = Direction * Speed;
                else
                    Velocity = Direction * Distance;
            }
            return Velocity;
        }
    }
}
